﻿namespace EFS.Common.Async
{
	/// <summary>
	/// Asyck root for calling async operations 
	/// </summary>
	public abstract class AsyncRoot : EFS.Common.ISelfDescriptor
	{
		/// <summary>
		/// Async method to be call 
		/// </summary>
		public delegate void ProcessWork();

		/// <summary>
		/// Async method to be call 
		/// </summary>
		public delegate void AsyncRootProcessCompleted(EFS.Common.Async.AsyncProcess ap);

		/// <summary>
		/// Describe this object
		/// </summary>
		/// <returns></returns>
		public abstract System.String Describe();

		/// <summary>
		/// Threading mode
		/// </summary>
		protected enum Threading
		{
			Pooled,
 			Worker,
			STA,
		}

		/// <summary>
		/// Constructor
		/// </summary>
		protected AsyncRoot()
		{
		}

		/// <summary>
		/// Unhandled exception
		/// </summary>
		/// <param name="ex"></param>
		protected abstract void OnException(System.Exception ex);

		/// <summary>
		/// Exception handler
		/// </summary>
		/// <param name="exceptionHandler"></param>
		/// <param name="ex"></param>
		protected void HandleExceptionSafe(System.Exception exception, EFS.Common.Async.AsyncException.AsyncExceptionHandler exceptionHandler = null)
		{
			// handler
			if (null == exceptionHandler)
			{
				exceptionHandler = OnException;
			}

			try
			{
				// call handler safe
				EFS.Common.Async.AsyncException asyncExceptionHandler = new AsyncException(exception, exceptionHandler);
				asyncExceptionHandler.Execute();
			}
#if !SILVERLIGHT
			catch (System.Exception ex)
			{
 				// log
				EFS.EventLog.Log.LogException(ex);
#else
			catch
			{
#endif
			}
		}

		/// <summary>
		/// Queue this root for async operation
		/// </summary>
		/// <param name="userCallBack"></param>
		/// <returns></returns>
		protected AsyncProcess BeginAsyncProcess2(ProcessWork processWork, Threading threading = Threading.Pooled)
		{
			// log
			// ~ commented for performance ~ EFS.EventLog.Log.LogInfo("BeginAsyncProcess", this);

			// process
			return BeginAsyncProcess2(processWork, AsyncProcessCallbackDefault, threading); 
		}

		/// <summary>
		/// Queue this root for async operation
		/// </summary>
		/// <param name="userCallBack"></param>
		/// <returns></returns>
		protected AsyncProcess BeginAsyncProcess2(ProcessWork processWork, AsyncRootProcessCompleted asyncProcessCompleted, Threading threading = Threading.Pooled)
		{
			// log
			// ~ commented for performance ~ EFS.EventLog.Log.LogInfo("BeginAsyncProcess", this);

			// check
			EFS.Common.Verify.Reference(processWork, "processWork");
			EFS.Common.Verify.Reference(asyncProcessCompleted, "asyncProcessCompleted");

			// process
			AsyncProcess asyncProcess = null;

			try
			{
				// process
				asyncProcess = new AsyncProcess(this, processWork, asyncProcessCompleted, OnException);

				// starttype
				switch (threading)
				{
#if !SILVERLIGHT
					case Threading.STA:
						{
							// start STA thread
							System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(ThreadProc));
							thread.SetApartmentState(System.Threading.ApartmentState.STA);
							thread.Start(asyncProcess);
							break;
						}
#endif
					case Threading.Worker:
						{
							// start non-STA thread
							System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(ThreadProc));
							thread.Start(asyncProcess);
							break;
						}
					default:
						{
							// make pooled thread
							System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(ThreadProc), asyncProcess);
							break;
						}
				}
			}
#if !SILVERLIGHT
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);
#else
			catch
			{
#endif
				// dispose
				asyncProcess = null;
			}

			// async process
			return asyncProcess;
		}


		/// <summary>
		/// Thread procedure callse up implementation
		/// </summary>
		/// <param name="obj"></param>
		internal static void ThreadProc(System.Object obj)
		{
			// state
			AsyncProcess asyncProcess = (AsyncProcess)obj;

			// call asynk method
			asyncProcess.RunAsyncProcess();

			// clean-up
			asyncProcess = null;
		}

		/// <summary>
		/// Default callback
		/// </summary>
		/// <param name="ap"></param>
		private static void AsyncProcessCallbackDefault(EFS.Common.Async.AsyncProcess ap)
		{
			// check
			ap.Throw();
		}
	}
}
